package com.zp.qualitypractice.list;

import android.animation.ValueAnimator;
import android.content.Context;
import android.support.annotation.Nullable;
import android.support.v4.view.NestedScrollingParent;
import android.support.v4.view.NestedScrollingParentHelper;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;


/**
 * Created by izp on 2017/4/11.
 */

public class RefreshLayout extends LinearLayout implements NestedScrollingParent{//继承一个嵌套滑动父类接口
    /*
    但是这和以前用过的dispatchTouchEvent,onInterceptTouchEvent,
    onTouchEvent和requestDisallowInterceptTouchEvent有什么区别呢?
    其实我感觉NestedScrollingParent和NestedScrollingChild是辅助解决事件冲突出现的.
    在之前的事件拦截中,就算子View的ACTION_MOVE的事件返回false,父布局也是获取不了该事件的.
    * */
    private Context mContext;
    private NestedScrollingParentHelper mParentNestScrollHelper;
    private LinearLayout mHeaderLayout ;

    private LinearLayout mFooterLayout ;

    private RefreshRecyclerView mRefreshRecyclerView;

    private OnPullLoadListener mOnPullLoadListener;

    private int mHeaderHeight = 0;
    private int mFooterHeight = 0;

    private int mTotalY = 0;

    private boolean isFling = false;//是否正在滑动，

    private boolean isRefresh = true;
    private boolean isLoad = true;//这两个始终为True的设定，让我感觉智商受到侮辱


    public RefreshLayout(Context context) {
        this(context,null);
    }

    public RefreshLayout(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,-1);
        this.mContext = context;
        init();
    }

    public RefreshLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
    private void init() {
        mParentNestScrollHelper = new NestedScrollingParentHelper(this);
        mHeaderLayout = new LinearLayout(mContext);
        mFooterLayout = new LinearLayout(mContext);
        mRefreshRecyclerView = new RefreshRecyclerView(mContext);

        setOrientation(VERTICAL);//linearLayout设置为垂直状态

        mHeaderLayout.setOrientation(VERTICAL);
        mFooterLayout.setOrientation(VERTICAL);

        addView(mHeaderLayout,new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
        addView(mRefreshRecyclerView,new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
        addView(mFooterLayout,new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));

    }

    public void setRefreshRecyclerView(RecyclerView.LayoutManager layoutManager, RecyclerView.Adapter adapter,@Nullable Boolean fixed){
        mRefreshRecyclerView.setLayoutManager(layoutManager);
        mRefreshRecyclerView.setAdapter(adapter);
        if (null!=fixed){
            mRefreshRecyclerView.setHasFixedSize(fixed);
        }
    }

    public void addHeaderView(View headerView,int headerHeight){
        this.mHeaderHeight = headerHeight;
        this.mHeaderLayout.removeAllViews();//清除headerLayout的所有子view
        this.mHeaderLayout.addView(headerView);
        LayoutParams headerLayoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,mHeaderHeight);
        headerLayoutParams.topMargin = -mHeaderHeight;//margin为负管用么？可以实现隐藏的效果么
        this.mHeaderLayout.setLayoutParams(headerLayoutParams);
    }

    public void addFooterView(View footerView, int footerHeight) {
        this.mFooterHeight = footerHeight;
        this.mFooterLayout.removeAllViews();
        this.mFooterLayout.addView(footerView);
        this.mFooterLayout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, footerHeight));
    }

    /**
     * @param fromY
     * @param toY
     * 把int类型的坐标值转换为float类型启用valueAnimator进行动画
     * 这是放手之后的滑动动画
     */
    public void setScrollTo(int fromY,int toY){
        smoothScrollTo((float) fromY, (float) toY);
//        scrollTo(0, toY);
    }

    /**
     * @param fromY
     * @param toY
     * 调用属性动画监听属性的变化，然后调用整个ViewGroup的ScrollTo
     */
    private void smoothScrollTo(float fromY,float toY){
        /*
        ValueAnimator是属性动画的核心类，最常用的ObjectAnimator就是它的子类。
        ValueAnimator本身不作用于任何对象，也就是说直接使用它没有任何动画效果。
        它可以对一个值做动画，然后我们可以监听其动画过程，在动画过程中修改我们的对象的属性值，
        这样也就相当于我们的对象做了动画。
        * */
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(new float[]{fromY,toY});//监听两个浮点值的变化？
        if (fromY == toY){//如果滑动距离等于0
            valueAnimator.setDuration(0);
        }else {
            valueAnimator.setDuration(300);
        }
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
//                这个valueAnimator，
                int to = (int)(-((Float)valueAnimator.getAnimatedValue()).floatValue());
                scrollTo(0, to);//this 整体移动
                mTotalY = to * 2;//乘以2是什么意思？
            }
        });
        valueAnimator.start();//动画开始！
    }

    public void setItemDivider(RecyclerView.ItemDecoration itemDecoration){
        mRefreshRecyclerView.addItemDecoration(itemDecoration);
    }

    public int getTotalY(){
        return -mTotalY / 2;
    }

    public void setScrollLoad(boolean isScrollLoad){
        mRefreshRecyclerView.isScrollLoad = isScrollLoad;
//        外部类可以获取内部类的私有变量，不能获取外部类的私有变量。
    }
    public boolean isScrollLoad(){
        return mRefreshRecyclerView.isScrollLoad;
    }
    public void setScrollRefresh(boolean isScrollRefresh){
        mRefreshRecyclerView.isScrollRefresh = isScrollRefresh;
    }
    public boolean isScrollRefresh(){
        return mRefreshRecyclerView.isScrollRefresh;
    }


    public void addOnTouchUpListener(OnPullLoadListener onPullLoadListener){
        this.mOnPullLoadListener = onPullLoadListener;
    }

    /**
     * 这个方法有点莫名其妙
     * 它是ViewGroup的方法，当嵌套滑动开始的之前调用
     * Parent 收到 onStartNestedScroll() 回调，决定是否需要配合 Child 一起进行处理滑动，
     * 如果需要配合，还会回调 onNestedScrollAccepted()。
     * @param child
     * @param target
     * @param nestedScrollAxes
     * @return
     */
    @Override
    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
//        return super.onStartNestedScroll(child, target, nestedScrollAxes);
//        就是拦不拦截嵌套滑动的意思？
        return true;//显然是子类需要父类配合嵌套滑动，返回true的花就会调用onNestedScrollAccepted
    }
    /*
    * 每次滑动前，Child 先询问 Parent 是否需要滑动，即 dispatchNestedPreScroll()，
    * 这就回调到 Parent 的 onNestedPreScroll()，Parent 可以在这个回调中“劫持”掉 Child 的滑动，也就是先于 Child 滑动。
    Child 滑动以后，会调用 onNestedScroll()，回调到 Parent 的 onNestedScroll()，
    这里就是 Child 滑动后，剩下的给 Parent 处理，也就是 后于 Child 滑动。
    * */

    /**
     *
     * @param child
     * @param target
     * @param axes
     */
    @Override
    public void onNestedScrollAccepted(View child, View target, int axes) {
//        super.onNestedScrollAccepted(child, target, axes);
//        把处理父View跟随子View滑动的实现交给helper
        mParentNestScrollHelper.onNestedScrollAccepted(child,target,axes);
    }

    /**
     * 每次滑动前，Child 先询问 Parent 是否需要滑动，即 dispatchNestedPreScroll()，
     * 这就回调到 Parent 的 onNestedPreScroll()，Parent 可以在这个回调中“劫持”掉 Child 的滑动，也就是先于 Child 滑动。
     Child 滑动以后，会调用 onNestedScroll()，回调到 Parent 的 onNestedScroll()，
     这里就是 Child 滑动后，剩下的给 Parent 处理，也就是 后于 Child 滑动。
     * @param target 引起了嵌套滑动的View
     * @param dx 水平滚动距离 向右滑动是负值,向左滑动是正值(起点-终点?)
     * @param dy 垂直滚动距离 向下滑动是负值,向上滑动是正值
     * @param consumed 输出值，其父父控件需要消耗的滑动距离
     *                 在子View滚动之前，父是否要滚动
     */
    @Override
    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
//        super.onNestedPreScroll(target, dx, dy, consumed);

        if (mTotalY < 0 && mRefreshRecyclerView.isOrientation(0) || mTotalY > 0 && mRefreshRecyclerView.isOrientation(1)) {
//            mRefreshRecyclerView.isOrientation这个方法就是判断能不能垂直滑动，没有其它作用
//            mTotalY大于0，是向下滑，小于0是向上滑
            isFling = true;//totalY应该是总的滑动值
        }
        if (isRefresh) {//这个始终为True，有毛用啊？
            if (dy > 0) {//这是当前滑动中的的y值还是滑动之前的y值
                if (mRefreshRecyclerView.isOrientation(0)) {//如果可以向下拉
                    mTotalY+= dy;//总y等于当前y累加？？？
                    if ((mTotalY / 2) <= 0) {//还是要赶快搞懂这个totalY是什么才行，
                        scrollTo(0, mTotalY / 2);//如果mTotal小于0，拉就滑动到小小于0的地方，滑动到屏幕外了啊！！
                        consumed[1] = dy;//consumed[1] 是输出的Y轴，consumed[0]应该是输出的X轴
                    }else {
                        scrollTo(0, 0);//滑动到整个View完全暂时的样子，也就是全部滑动大于0时，会滑动到整个View全部暂时
                        consumed[1] = 0;//初始值就是0
                    }
                }
                return;//如果不可以向下滑动调出方法
            }
        }
        if (isLoad){
            if (dy < 0) {
                if (mRefreshRecyclerView.isOrientation(1)) {//是否能向上滑动
                    mTotalY += dy;
                    if ((mTotalY / 2) >= 0) {
                        scrollTo(0, mTotalY / 2);
                        consumed[1] = dy;
                    } else {
                        scrollTo(0, 0);
                        consumed[1] = 0;
                    }
                }
                return;
            }
        }
    }

    /**
     * @param target
     * @param dxConsumed
     * @param dyConsumed
     * @param dxUnconsumed
     * @param dyUnconsumed
     */
    @Override
    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
//        super.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
        if (dyUnconsumed != 0) {//如果y轴的消费不等于0
            mTotalY += dyUnconsumed;
            scrollTo(0, mTotalY / 2);
        }
    }

    /**
     * @param child
     */
    @Override
    public void onStopNestedScroll(View child) {
//        super.onStopNestedScroll(child);
        mParentNestScrollHelper.onStopNestedScroll(child);
        if (mOnPullLoadListener != null) {
            isFling = false;//如果监听为空，那么滑动状态也为false
            if (this.getTotalY() >= mHeaderHeight) {//如果滑动的值大于header的高度
                this.setScrollTo(this.getTotalY(), mHeaderHeight);//最后还是回到header高度的地方
                if (!this.isScrollRefresh()) {//如果当前不在刷新中
                    this.setScrollRefresh(true);
                    mOnPullLoadListener.onRefreshing();//开始刷新
                    this.setScrollRefresh(false);
                    this.setScrollTo(this.getTotalY(), 0);//不是已经回到headerHeight的位置了么，怎么还要从getTotalY开始滑动
                }
            }else if (-this.getTotalY() >= mFooterHeight){//
                this.setScrollTo(this.getTotalY(), -mFooterHeight);
                if (!this.isScrollLoad()) {
                    this.setScrollLoad(true);
                    mOnPullLoadListener.onLoading();
                    this.setScrollLoad(false);
                    this.setScrollTo(this.getTotalY(), 0);
                }
            }else {
                this.setScrollTo(0, 0);//滑动到初始位置
            }
        }
    }
    @Override
    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
//        return super.onNestedPreFling(target, velocityX, velocityY);
        return isFling;
    }
    /**
     * @param target
     * @param velocityX
     * @param velocityY
     * @param consumed
     * @return
     * 你可以捕获对内部View的fling事件，如果return true则表示拦截掉内部View的事件。
     */
    @Override
    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
//        return super.onNestedFling(target, velocityX, velocityY, consumed);
        return isFling;//抛掷
    }

    @Override
    public int getNestedScrollAxes() {
//        return super.getNestedScrollAxes();
        return mParentNestScrollHelper.getNestedScrollAxes();
    }

    private class RefreshRecyclerView extends RecyclerView{
        private StaggeredGridLayoutManager mStaggeredGridLayoutManager;
        private LinearLayoutManager mLinearLayoutManager;
        private GridLayoutManager mGridLayoutManager;
        private LayoutManager mLayoutManager;//这个没有 mLayoutManager.getOrientation()方法，所以需要强转成各自的类型

        private boolean isScrollLoad = false;//当前状态？还是什么？
        private boolean isScrollRefresh = false;

        public RefreshRecyclerView(Context context) {
            super(context);
            setVerticalFadingEdgeEnabled(false);//关闭垂直滑动阴影
            setHorizontalFadingEdgeEnabled(false);//关闭水平滑动阴影滚动条
            setVerticalScrollBarEnabled(false);//关闭垂直滑动滚动条
            setHorizontalScrollBarEnabled(false);//关闭水平滑动滚动条

            setOverScrollMode(OVER_SCROLL_NEVER);//永远不超滑
            setItemAnimator(new DefaultItemAnimator());//使用默认动画

        }

//        public RefreshRecyclerView(Context context, @Nullable AttributeSet attrs) {
//            super(context, attrs);
//        }
//
//        public RefreshRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
//            super(context, attrs, defStyle);
//        }
        private void setRefreshLayoutManager(LayoutManager layoutManager){
            if (layoutManager instanceof StaggeredGridLayoutManager){//如果是瀑布流式的布局
                mStaggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;//这尼玛是什么鬼，为毛要分别强转呢？
//                mLayoutManager = (StaggeredGridLayoutManager) layoutManager;
            } else if (layoutManager instanceof GridLayoutManager) {
                mGridLayoutManager = (GridLayoutManager) layoutManager;
            } else if (layoutManager instanceof LinearLayoutManager) {
                mLinearLayoutManager = (LinearLayoutManager) layoutManager;
            }
            setLayoutManager(layoutManager);//并没有对强转之后的layoutManager进行任何操作，还是直接使用了传入的layoutManager
            if (!isVertical()){//如果不是垂直
                throw new NullPointerException("NoVerticalLayout");//直接抛出异常这样好么？
            }
        }

        private boolean isOrientation(int orientation){
            if (orientation==0){
                return isCanPullDown();
            }else if (orientation==1){
                return isCanPullUp();
            }else {
                return false;
            }
        }

        private boolean isCanPullDown(){
            return !canScrollVertically(-1);
        }

        private boolean isCanPullUp(){
            return !canScrollVertically(1);
        }

        private boolean isVertical() {//通过LayoutManger获取当前Recycler的方向
            if (mStaggeredGridLayoutManager != null)
                return mStaggeredGridLayoutManager.getOrientation() == StaggeredGridLayoutManager.VERTICAL;
            else if (mLinearLayoutManager != null)
                return mLinearLayoutManager.getOrientation() == LinearLayoutManager.VERTICAL;
            else if (mGridLayoutManager!= null)
//                mLayoutManager.getOrientation()纯粹的layoutManager没有getOrientation的方法，所以前面要强转
                return mGridLayoutManager.getOrientation() == GridLayoutManager.VERTICAL;
            return false;
        }

    }
}
